{
  // 实现一个原型继承的方法

  // 法一 原型链继承（不推荐）
  function Animal(name) {
    this.name = name || '狗';
  }
  Animal.prototype.sayHello = function () {
    console.log('hello 我是汪星人');
  };

  function Cat() {}
  Cat.prototype = new Animal();
  Cat.prototype.say = function () {
    console.log('我是喵星人');
  };

  let cat = new Cat();
  console.log(cat.name); // 狗
  cat.say(); // 我是喵星人 子类方法
  cat.sayHello(); // hello 我是汪星人 父类方法
  console.log(cat instanceof Cat); // true
  console.log(cat instanceof Animal); // true

  // 不推荐使用
  // 缺点
  // 1 无法向父类构造函数传参
  // 2 父类的所有属性被共享，只要一个实例修改了属性，其他是所有子类实例都会被影响

  // 法二 借用构造函数（经典继承）（不推荐）
  function Animal(name) {
    this.name = name || '狗';
  }
  Animal.prototype.sayHello = function () {
    console.log('hello 我是汪星人');
  };

  function Cat(age, name) {
    Animal.call(this, name);
    this.age = age;
  }

  let cat = new Cat(18, '猫');
  console.log(cat.name); // 猫
  console.log(cat.age); // 18
  // cat.sayHello() //  cat.sayHello is not a function
  console.log(cat instanceof Cat); // true
  console.log(cat instanceof Animal); // false

  // 优点 1 可以为父类传参 2 避免了共享属性
  // 缺点 1 只是子类的实例，不是父类的实例，方法都定义在构造函数中，每次创建实例都需要创建一遍方法

  // 法三 组合继承（推荐） 组合原型链继承和构造函数继承
  function Animal(name) {
    this.name = name || '狗';
    console.log(111); // 调用两次父类构造函数
  }
  Animal.prototype.sayHello = function () {
    console.log('hello 我是汪星人');
  };

  function Cat(age, name) {
    Animal.call(this, name);
    this.age = age;
  }

  Cat.prototype = new Animal();
  Cat.prototype.contructor = Cat; // 修复构造函数的指向

  let cat = new Cat('小鸭子', 18);
  console.log(cat.name);
  console.log(cat.age);
  console.log(cat instanceof Cat); // true
  console.log(cat instanceof Animal); // true

  // 优点：融合原型继承和构造函数继承的优点，是js中最常见的继承
  // 缺点：调用了两次父类构造函数

  // 法四 原型式继承（不推荐）
  function create(o) {
    function F() {}
    F.prototype = o;
    return new F();
  }

  let obj = {
    gift: ['a', 'b'],
  };

  let jon = create(obj);
  let xiaoming = create(obj);

  jon.gift.push('c');
  xiaoming.gift; // ['a', 'b', 'c']

  // 缺点：共享了属性和方法

  // 法五 寄生式继承
  // 创建一个用于封装继承过程的函数，该函数在内部以某种形式来增强对象，最后返回对象
  function create(o) {
    var clone = Object.create(o);
    clone.say = function () {
      console.log('hello');
    };

    return clone;
  }
  // 缺点跟借用构造函数模式一样，每次创建都会创建一遍方法

  // 法六 寄生组合继承（最佳）
  // 子类构造函数复制父类自身的属性和方法，子类原型只接受父类的原型和方法

  function Animal(name) {
    this.name = name || '狗';
    console.log(111); // 执行一次构造函数
  }
  Animal.prototype.sayHello = function () {
    console.log('hello 我是汪星人');
  };

  // function create(prototype) {
  //   function Super() { }
  //   Super.prototype = prototype
  //   return Super
  // }

  function Cat(age, name) {
    Animal.call(this, name);
    this.age = age;
  }

  Cat.prototype = Object.create(Animal.prototype); // 创建Animal构造函数的原型的副本
  Cat.prototype.constructor = Cat; // 修复构造函数执行

  let cat = new Cat(18, '小猪');
  console.log(cat.name); // 小猪
  console.log(cat.age); // 18
  console.log(cat instanceof Cat); // true
  console.log(cat instanceof Animal); // true

  // 寄生组合继承只调用了一次父类构造函数，并且避免了父类原型(prototype)上面创建不必要的多余的属性。
  // 与此同时原型链保持不变，还能够正常使用 instanceof 和 isPrototypeOf。开发人员普遍认为寄生组合式继承是引用类型最理想的继承范式。

  // 法七
  // 寄生组合继承有一个缺陷
  // 能自己修改 constructor，别人也能修改 constructor，别人不小心修改了 constructor，就出问题了
  function Animal(name) {
    this.name = name || '狗';
    console.log(111); // 执行一次构造函数
  }
  Animal.attr = '喵喵'; // 静态属性

  Animal.prototype.sayHello = function () {
    console.log('hello 我是汪星人');
  };

  function Cat(age, name) {
    Animal.call(this, name);
    this.age = age;
  }

  Cat.prototype = Object.create(Animal.prototype, {
    constructor: {
      value: Cat,
      writable: false,
    },
    test: {
      value: function () {},
    },
  });
  // 继承静态属性
  const statickeys = Object.entries(Animal);
  statickeys.forEach(([key, value]) => {
    Cat[key] = value;
  });

  // 法八 es6 extends继承
  class Animal {
    constructor(name) {
      this.name = name || '狗';
    }

    sayHello() {
      console.log('hello 我是汪星人');
    }
  }

  class Cat extends Animal {
    constructor(age, name) {
      super(name);
      this.age = age;
    }
    say() {
      console.log('我是喵星人');
    }
  }

  let cat = new Cat(18, '猫');
  console.log(cat.name);
  console.log(cat.age);
  console.log(cat instanceof Cat); // true
  console.log(cat instanceof Animal); // true
  // 优点：不用动态设置原型
  // 缺点：es6 语法 兼容性不是很好，需要babel转换成es5代码
}
